/*
 * Long term port to Java.
 * Copyright (C) 1999  Christopher Edwards
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
package javaforce.codec.gsm;

public class Long_term {

  public void Gsm_Long_Term_Predictor(
          short[] d, /* [0..39]   residual signal    IN      */
          int k, /* d   entry point, which 40            */
          short[] e, /* [0..39] add 5 to index       OUT     */
          short[] dp,
          short[] dpp,
          int dp_dpp_point_dp0,
          short[] Nc, /* correlation lag              OUT     */
          short[] bc, /* gain factor                  OUT     */
          int Nc_bc_index) {
    Calculation_of_the_LTP_parameters(
            d, k, dp, dp_dpp_point_dp0,
            bc, Nc, Nc_bc_index);
    Long_term_analysis_filtering(
            bc[Nc_bc_index], Nc[Nc_bc_index],
            dp, d, k, dpp, e, dp_dpp_point_dp0);
  }

  private void Calculation_of_the_LTP_parameters(
          short[] d, /* [0..39]      IN      */
          int d_index,
          short[] dp, /* [-120..-1]   IN      */
          int dp_start,
          short[] bc_out, /*              OUT     */
          short[] Nc_out, /*              OUT     */
          int Nc_bc_index)
          throws IllegalArgumentException {

    int lambda = 0;
    short Nc = 0;
    short[] wt = new short[40];

    int L_max = 0, L_power = 0;
    short R = 0, S = 0, dmax = 0, scal = 0;
    short temp = 0;

    /*  Search of the optimum scaling of d[0..39].
     */
    for (int k = 0; k <= 39; k++) {
      temp = d[k + d_index];
      temp = Add.GSM_ABS(temp);
      if (temp > dmax) {
        dmax = temp;
      }
    }

    temp = 0;

    if (dmax == 0) {
      scal = 0;
    } else {
      if (!(dmax > 0)) {
        throw new IllegalArgumentException("Calculation_of_the_LTP_parameters: dmax = "
                + dmax + " should be > 0.");
      }
      temp = Add.gsm_norm(dmax << 16);
    }

    if (temp > 6) {
      scal = 0;
    } else {
      scal = (short) (6 - temp);
    }

    if (!(scal >= 0)) {
      throw new IllegalArgumentException("Calculation_of_the_LTP_parameters: scal = "
              + scal + " should be >= 0.");
    }


    /*  Initialization of a working array wt
     */
    for (int k = 0; k <= 39; k++) {
      wt[k] = Add.SASR(d[k + d_index], scal);
    }

    /* Search for the maximum cross-correlation and coding of the LTP lag
     */
    L_max = 0;
    Nc = 40;
    /* index for the maximum cross-correlation */

    for (lambda = 40; lambda <= 120; lambda++) {
      int L_result = 0;
      int step = 1;

      L_result = STEP(0, wt, dp, dp_start - lambda);
      L_result += STEP(1, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(2, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(3, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(4, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(5, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(6, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(7, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(8, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(9, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(10, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(11, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(12, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(13, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(14, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(15, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(16, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(17, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(18, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(19, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(20, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(21, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(22, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(23, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(24, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(25, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(26, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(27, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(28, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(29, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(30, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(31, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(32, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(33, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(34, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(35, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(36, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(37, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(38, wt, dp, step + dp_start - lambda);
      step++;
      L_result += STEP(39, wt, dp, step + dp_start - lambda);
      step++;

      if (L_result > L_max) {
        Nc = (short) lambda;
        L_max = L_result;
      }
    }

    Nc_out[Nc_bc_index] = Nc;

    L_max <<= 1;

    /*  Rescaling of L_max
     */
    if (!(scal <= 100 && scal >= -100)) {
      throw new IllegalArgumentException("Calculation_of_the_LTP_parameters: scal = "
              + scal + " should be >= -100 and <= 100.");
    }

    L_max = L_max >> (6 - scal);
    /* sub(6, scal) */

    if (!(Nc <= 120 && Nc >= 40)) {
      throw new IllegalArgumentException("Calculation_of_the_LTP_parameters: Nc = "
              + Nc + " should be >= 40 and <= 120.");
    }


    /*   Compute the power of the reconstructed short term residual
		 *   signal dp[..]
     */
    L_power = 0;
    for (int k = 0; k <= 39; k++) {
      int L_temp;

      L_temp = Add.SASR(dp[k - Nc + dp_start], 3);
      L_power += L_temp * L_temp;
    }
    L_power <<= 1;
    /* from L_MULT */

 /*  Normalization of L_max and L_power
     */

    if (L_max <= 0) {
      bc_out[Nc_bc_index] = 0;
      return;
    }
    if (L_max >= L_power) {
      bc_out[Nc_bc_index] = 3;
      return;
    }

    temp = Add.gsm_norm(L_power);

    R = Add.SASR(L_max << temp, 16);
    S = Add.SASR(L_power << temp, 16);

    /*  Coding of the LTP gain
     */

 /*  Table 4.3a must be used to obtain the level DLB[i] for the
		 *  quantization of the LTP gain b to get the coded version bc.
     */
    for (int bc = 0; bc <= 2; bc++) {
      if (R <= Add.GSM_MULT(S, Gsm_Def.gsm_DLB[bc])) {
        break;
      }
      bc_out[Nc_bc_index] = (short) bc;
    }
  }

  private int STEP(int k, short[] wt, short[] dp, int dp_i) {
    return (wt[k] * dp[dp_i]);
  }

  /*
	 *  In this part, we have to decode the bc parameter to compute
	 *  the samples of the estimate dpp[0..39].  The decoding of bc
	 *  needs the use of table 4.3b.  The long term residual signal
	 *  e[0..39] is then calculated to be fed to the  RPE  encoding
	 *  section.
   */
  static void Long_term_analysis_filtering(
          short bc, /*                                      IN  */
          short Nc, /*                                      IN  */
          short[] dp, /* previous d   [-120..-1]              IN  */
          short[] d, /* d            [0..39]                 IN  */
          int d_index,
          short[] dpp, /* estimate     [0..39]                 OUT */
          short[] e, /* long term res. signal [0..39]        OUT */
          int dp_dpp_index) {
    short BP = 0;

    switch (bc) {
      case 0:
        BP = (short) 3277;
        for (int k = 0; k <= 39; k++) {
          dpp[k + dp_dpp_index] = Add.GSM_MULT_R(BP,
                  dp[k - Nc + dp_dpp_index]);
          e[k + 5] = Add.GSM_SUB(d[k + d_index],
                  dpp[k + dp_dpp_index]);
        }
        break;

      case 1:
        BP = (short) 11469;
        for (int k = 0; k <= 39; k++) {
          dpp[k + dp_dpp_index] = Add.GSM_MULT_R(BP,
                  dp[k - Nc + dp_dpp_index]);
          e[k + 5] = Add.GSM_SUB(d[k + d_index],
                  dpp[k + dp_dpp_index]);
        }
        break;

      case 2:
        BP = (short) 21299;
        for (int k = 0; k <= 39; k++) {
          dpp[k + dp_dpp_index] = Add.GSM_MULT_R(BP,
                  dp[k - Nc + dp_dpp_index]);
          e[k + 5] = Add.GSM_SUB(d[k + d_index],
                  dpp[k + dp_dpp_index]);
        }
        break;

      case 3:
        BP = (short) 32767;
        for (int k = 0; k <= 39; k++) {
          dpp[k + dp_dpp_index] = Add.GSM_MULT_R(BP,
                  dp[k - Nc + dp_dpp_index]);
          e[k + 5] = Add.GSM_SUB(d[k + d_index],
                  dpp[k + dp_dpp_index]);
        }
        break;
    }
  }

  /*
	 *  This procedure uses the bcr and Ncr parameter to realize the
	 *  long term synthesis filtering.  The decoding of bcr needs
	 *  table 4.3b.
   */
  public void Gsm_Long_Term_Synthesis_Filtering(
          Gsm_State S,
          short Ncr,
          short bcr,
          short[] erp, /* [0..39]                IN  */
          int dp0_index_start_drp
  /* [-120..-1] IN, [0..40] OUT */
  /* drp is a pointer into the Gsm_State
		 * dp0 short array. */
  )
          throws IllegalArgumentException {
    int ltmp;
    /* for ADD */
    short brp, drpp, Nr;
    short[] drp = S.getDp0();

    /*  Check the limits of Nr.
     */
    Nr = Ncr < 40 || Ncr > 120 ? S.getNrp() : Ncr;

    S.setNrp(Nr);

    if (!(Nr >= 40 && Nr <= 120)) {
      throw new IllegalArgumentException("Gsm_Long_Term_Synthesis_Filtering Nr = "
              + Nr + " is out of range. Should be >= 40 and <= 120");
    }

    /*  Decoding of the LTP gain bcr
     */
    brp = Gsm_Def.gsm_QLB[bcr];

    /*  Computation of the reconstructed short term residual
		 *  signal drp[0..39]
     */
    if (brp == Gsm_Def.MIN_WORD) {
      throw new IllegalArgumentException("Gsm_Long_Term_Synthesis_Filtering brp = "
              + brp + " is out of range. Should be = " + Gsm_Def.MIN_WORD);
    }

    for (int k = 0; k <= 39; k++) {
      drpp = Add.GSM_MULT_R(brp, drp[k - Nr + dp0_index_start_drp]);
      drp[k + dp0_index_start_drp] = Add.GSM_ADD(erp[k], drpp);
    }

    /*
		 *  Update of the reconstructed short term residual signal
		 *  drp[ -1..-120 ]
     */
    System.arraycopy(drp, (dp0_index_start_drp - 80),
            drp, (dp0_index_start_drp - 120), 120);

    S.setDp0(drp);
  }
}
